package lanqiao.真题.第13届Java大学B组;

import com.sun.jmx.remote.internal.ArrayQueue;

import java.io.*;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.StringTokenizer;

/**
 * @author 挚爱之夕
 * @version 1.0
 * @implSpec 问题描述
 * 小明有一个大小为N×M的矩阵，可以理解为一个N行M列的二维数组。
 * 我们定义一个矩阵m的稳定度f(m）为f(m)=max(m)-min(m),其中max(m)表示矩阵m中的最大值，min(m)表示矩阵m中的最小值。
 * 现在小明想要从这个矩阵中找到一个稳定度不大于limit的子矩阵，同时他还希望这个子矩阵的面积越大越好（面积可以理解为矩阵中元素个数）。
 * 子矩阵定义如下：从原矩阵中选择一组连续的行和一组连续的列，这些行列交点上的元素组成的矩阵即为一个子矩阵。
 * <p>
 * 【输入格式】
 * 第一行输入两个整数N,M,表示矩阵的大小。
 * 接下来N行，每行输入M个整数，表示这个矩阵。
 * 最后一行输入一个整数limit,表示限制。
 * 【输出格式】
 * 输出一个整数，分别表示小明选择的子矩阵的最大面积。
 * <p>
 * 【样例输入】
 * 3	4
 * 2	0	7	9
 * 0	6	9	7
 * 8	4	6	4
 * 8
 * <p>
 * 【样例输出】
 * 6
 * 思路：
 * 1.枚举行号 x1 x2
 * 2.二分找最小宽
 * 3.使用辅助数组 将二维压缩成一维 滑动窗口判断是否满足情况
 * @since 2023 - 03 - 09 - 14:30
 */
public class F_最大子矩阵 {
}

class Main_F {
    static int N, M;
    static int limit;
    //预处理
    static int[][][] minHelper; //minHelper[k][x][y]  matrix第k列 x 到 y行 的最小值
    static int[][][] maxHelper; //maxHelper[k][x][y]  matrix第k列 x 到 y行 的最大值
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out));
    static StringTokenizer tokenizer = new StringTokenizer("");

    static String readLine() throws IOException {
        return reader.readLine();
    }

    static int nextInt() throws IOException {
        if (!tokenizer.hasMoreTokens()) {
            tokenizer = new StringTokenizer(readLine());
        }
        return Integer.parseInt(tokenizer.nextToken());
    }

    public static void main(String[] args) throws IOException {
        N = nextInt();
        M = nextInt();

        minHelper = new int[M + 1][N + 1][N + 1];
        maxHelper = new int[M + 1][N + 1][N + 1];

        int res = 0;
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= M; j++) {
                maxHelper[j][i][i] = minHelper[j][i][i] = nextInt();
            }
        }
        limit = nextInt();

        //预处理
        for (int k = 1; k <= M; k++) {
            for (int i = 1; i <= N; i++) {
                for (int j = i + 1; j <= N; j++) {
                    maxHelper[k][i][j] = Math.max(maxHelper[k][j][j], maxHelper[k][i][j - 1]);
                    minHelper[k][i][j] = Math.min(minHelper[k][j][j], minHelper[k][i][j - 1]);
                }
            }
        }

        //枚举行
        for (int x1 = 1; x1 <= N; x1++) {
            for (int x2 = x1; x2 <= N; x2++) {
                //二分找满足情况的最大宽
                int l = 1, r = M;
                while (l < r) {
                    int mid = (l + r + 1) >> 1;//l + ((r - l + 1) >> 1)
                    if (check(x1, x2, mid)) {
                        l = mid;
                    } else {
                        r = mid - 1;
                    }
                }

                if (check(x1, x2, r)) {
                    res = Math.max(res, (x2 - x1 + 1) * r);
                }
            }
        }
        writer.println(res);
        writer.flush();
    }

    /**
     * 双端队列 求 w宽的滑动窗口 是否满足情况
     * @param x1 矩阵行
     * @param x2 矩阵行
     * @param w  滑动窗口宽
     * @return 是否存在 以 x1 x2 为上下边， 宽为w的子矩阵满足情况
     */
    static boolean check(int x1, int x2, int w) {
        Deque<Integer> minQueue = new ArrayDeque<>();
        Deque<Integer> maxQueue = new ArrayDeque<>();
        for (int i = 1; i <= M; i++) {
            //处理最小
            if (!minQueue.isEmpty() && minQueue.peekFirst() < i - w + 1) minQueue.pollFirst();
            while (!minQueue.isEmpty() && minHelper[minQueue.peekLast()][x1][x2] > minHelper[i][x1][x2])
                minQueue.pollLast();
            minQueue.offerLast(i);

            //处理最大
            if (!maxQueue.isEmpty() && maxQueue.peekFirst() < i - w + 1) maxQueue.pollFirst();
            while (!maxQueue.isEmpty() && maxHelper[maxQueue.peekLast()][x1][x2] < maxHelper[i][x1][x2])
                maxQueue.pollLast();
            maxQueue.offerLast(i);

            //说明窗口为k
            if (i >= w && maxHelper[maxQueue.peekFirst()][x1][x2] - minHelper[minQueue.peekFirst()][x1][x2] <= limit)
                return true;
        }
        return false;
    }
}

class Main_F1 {
    //max[k][i][j]表示第k列中[i,j]之间的最大值
    static int[][][] max;
    static int[][][] min;
    static int n, m, limit, ans;
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) throws IOException {
        String[] s = br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        m = Integer.parseInt(s[1]);
        max = new int[m + 1][n + 1][n + 1];
        min = new int[m + 1][n + 1][n + 1];
        for (int i = 1; i <= n; i++) {
            s = br.readLine().split(" ");
            for (int j = 1; j <= m; j++) {
                max[j][i][i] = min[j][i][i] = Integer.parseInt(s[j - 1]);
            }
        }
        limit = Integer.parseInt(br.readLine());

        //预处理  复杂度 n^2*m
        for (int k = 1; k <= m; ++k) {
            for (int i = 1; i <= n; ++i) {
                for (int j = i + 1; j <= n; ++j) {
                    max[k][i][j] = Math.max(max[k][i][j - 1], max[k][j][j]);
                    min[k][i][j] = Math.min(min[k][i][j - 1], min[k][j][j]);
                }
            }
        }

        for (int x1 = 1; x1 <= n; x1++) {
            for (int x2 = x1; x2 <= n; x2++) {
                int l = 1, r = m;
                while (l < r) {
                    int mid = l + r + 1 >> 1;
                    if (check(x1, x2, mid)) l = mid;
                    else r = mid - 1;
                }
                if (check(x1, x2, r)) ans = Math.max(ans, (x2 - x1 + 1) * r);
            }
        }

        out.println(ans);
        out.flush();
    }

    //k是窗口大小
    static boolean check(int x1, int x2, int k) {
        Deque<Integer> qmax = new ArrayDeque<>();
        Deque<Integer> qmin = new ArrayDeque<>();
        for (int i = 1; i <= m; i++) {
            //处理最小
            if (!qmin.isEmpty() && qmin.peekFirst() < i - k + 1) qmin.pollFirst();
            while (!qmin.isEmpty() && min[qmin.peekLast()][x1][x2] > min[i][x1][x2]) qmin.pollLast();
            qmin.offerLast(i);

            //处理最大
            if (!qmax.isEmpty() && qmax.peekFirst() < i - k + 1) qmax.pollFirst();
            while (!qmax.isEmpty() && max[qmax.peekLast()][x1][x2] < max[i][x1][x2]) qmax.pollLast();
            qmax.offerLast(i);
            //说明窗口为k
            if (i >= k && max[qmax.peekFirst()][x1][x2] - min[qmin.peekFirst()][x1][x2] <= limit) return true;
        }
        return false;
    }
}